package elephant.rpc.client.core;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import elephant.rpc.client.RPCClient;
import elephant.rpc.server.message.RPCMessage;
import elephant.rpc.server.message.RPCRequest;
import elephant.rpc.server.session.RPCSession;
import elephant.utils.DumpUtil;

/**
 * 
 * @author skydu
 *
 */
public abstract class RPCInvocationHandler implements InvocationHandler{
	
	private static Logger logger=LoggerFactory.getLogger(RPCInvocationHandler.class);
	//
	protected RPCClient client;
	/**一个集群对应多个连接*/
	protected RPCSession[] sessions;
	
	private int invokeCount=0;
	private int sessionCount;
	
	public RPCInvocationHandler(
			RPCClient client,
			List<RPCSession> sessionList){
		this.client=client;
		this.sessions=sessionList.toArray(new RPCSession[sessionList.size()]);
		this.sessionCount=sessions.length;
	}
	//
	@Override
	public Object invoke(Object proxy,Method method, Object[] args)
			throws Throwable {
		//寻轮策略
		int index=(invokeCount++)%sessionCount;
		RPCSession session=sessions[index];
		if(!session.isConnected()){
			for(int i=0;i<sessionCount;i++){
				session=sessions[i];
				if(session.isConnected()){
					break;
				}
			}
		}
		//
		if(!session.isConnected()){
			logger.warn("no alive remote node.{}:{}",session.remoteHost,session.remotePort);
			throw new RuntimeException("no alive remote node."+session.remoteHost+":"+session.remotePort);
		}
		//
		String serviceId=method.getDeclaringClass().getSimpleName()+"."+method.getName();
		//
		Class<?>pp[]=method.getParameterTypes();
		String pt[]=new String[pp.length];
		for(int i=0;i<pt.length;i++){
			pt[i]=pp[i].getSimpleName();
		}
		RPCMessage msg=RPCMessage.createReqMsg(
				client.messageId.getAndIncrement(),
				method.getDeclaringClass().getSimpleName(),
				method.getName(), 
				pt,
				args);
		//
		RPCRequest req=new RPCRequest();
		req.setStartTime(System.currentTimeMillis());
		req.setSession(session);
		req.setMessage(msg);
		//
		if(logger.isInfoEnabled()){
			logger.info(">>>invoke {}",serviceId);
		}
		if(logger.isDebugEnabled()){
			String traceName="#"+msg.id+
					"@"+">"+
					RPCMessage.getServiceClass(msg)+"."+RPCMessage.getMethodName(msg);
			logger.debug(DumpUtil.dumpParameter(traceName,msg.parameters));
		}
		//
		Object result=invoke0(req,method);
		if(logger.isInfoEnabled()){
			int useTime= (int)(System.currentTimeMillis()-req.getStartTime());
			logger.info("<<<invoke {} useTime:{}ms {}",serviceId,useTime,
						req.getMessage().isAsync?"isAsync":"");
		}
		return result;
	}
	//
	protected abstract Object invoke0(RPCRequest req,Method method)throws Throwable;

}
